#ifndef __ESP_I2C_H__
#define __ESP_I2C_H__
#include <bit>

#include "esp_err.h"
#include "esp_log.h"
#include "driver/i2c_master.h"

#define USE_ESP_I2C

class EspI2C
{
public:
    static EspI2C *GetInstance()
    {
        static EspI2C s_instance;
        return &s_instance;
    }

public:
    EspI2C() : m_bus_handle(nullptr) {}
    ~EspI2C() { Deinit(); }

    void Init(i2c_port_num_t i2c_port, gpio_num_t sda_io_num, gpio_num_t scl_io_num)
    {
        if (m_bus_handle != nullptr)
        {
            ESP_LOGW("EspI2C", "i2c%d has been inited", (int)i2c_port);
            return;
        }

        i2c_master_bus_config_t bus_config = {
            .i2c_port = i2c_port,
            .sda_io_num = sda_io_num,
            .scl_io_num = scl_io_num,
            .clk_source = I2C_CLK_SRC_DEFAULT,
            .glitch_ignore_cnt = 7,
        };
        bus_config.flags.enable_internal_pullup = GPIO_PULLUP_ENABLE;

        ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &m_bus_handle));
    }

    void Deinit()
    {
        if (m_bus_handle != nullptr)
        {
            ESP_ERROR_CHECK(i2c_del_master_bus(m_bus_handle));
            m_bus_handle = nullptr;
        }
    }

    void Reset()
    {
        ESP_ERROR_CHECK(i2c_master_bus_reset(m_bus_handle));
    }

    i2c_master_dev_handle_t AddDevice(uint8_t device_address, uint32_t scl_speed_hz)
    {
        i2c_device_config_t device_config = {
            .dev_addr_length = I2C_ADDR_BIT_LEN_7,
            .device_address = device_address,
            .scl_speed_hz = scl_speed_hz,
        };
        device_config.flags.disable_ack_check = 0;
        i2c_master_dev_handle_t device_handle = nullptr;
        ESP_ERROR_CHECK(i2c_master_bus_add_device(m_bus_handle, &device_config, &device_handle));
        return device_handle;
    }

    void DelDevice(i2c_master_dev_handle_t device_handle)
    {
        ESP_ERROR_CHECK(i2c_master_bus_rm_device(device_handle));
    }

    esp_err_t Read(i2c_master_dev_handle_t device_handle, uint8_t *data, size_t size, int timeout_ms = 100)
    {
        return i2c_master_receive(device_handle, data, size, timeout_ms);
    }

    esp_err_t Write(i2c_master_dev_handle_t device_handle, const uint16_t data, int timeout_ms = 100)
    {
        uint8_t bytes[2];
        if constexpr (std::endian::native == std::endian::big)
        {
            // 0x12345678 => 12 34 56 78
            bytes[0] = (uint8_t)(data & 0x00FF);
            bytes[1] = (uint8_t)((data >> 8) & 0x00FF);
        }
        else
        {
            // 0x12345678 => 78 56 34 12
            bytes[0] = (uint8_t)((data >> 8) & 0x00FF);
            bytes[1] = (uint8_t)(data & 0x00FF);
        }

        // 网络传输一般使用大端, 这里也使用大端传输
        return i2c_master_transmit(device_handle, bytes, 2, timeout_ms);
    }

    esp_err_t Write(i2c_master_dev_handle_t device_handle, const uint8_t *data, size_t size, int timeout_ms = 100)
    {
        return i2c_master_transmit(device_handle, data, size, timeout_ms);
    }

    esp_err_t WriteRead(i2c_master_dev_handle_t device_handle, const uint8_t *write_data, size_t write_size, uint8_t *read_data, size_t read_size, int timeout_ms = 100)
    {
        return i2c_master_transmit_receive(device_handle, write_data, write_size, read_data, read_size, timeout_ms);
    }

protected:
    i2c_master_bus_handle_t m_bus_handle;
};
#endif